<template>
  <div class="client-chat" style="height: 98%">
    <div class="qa-item" v-for="(item, index) in msgList" :key="index">
      <!-- 问题 -->
      <div class="question-item" v-if="item.is_from_self">
        <v-spinner
          status="default"
          class="qs-loading"
          v-if="item.is_loading"
        ></v-spinner>
        <VueMarkdown
          class="question-text"
          style="max-width: 352px"
          :source="item.content"
          :anchorAttributes="{ target: '_blank' }"
          :linkify="false"
        />
      </div>
      <!-- 以下列出的常见问题可以横向滑动 -->
      <div v-if="item.issue" class="chat-content1">
        <div
          class="quick-questions"
          :key="quIdx"
          v-for="(quIdx, idx) in item.quickQuestions.length"
          scroll-x
        >
          <div
            v-for="(question, index) in item.quickQuestions[idx]"
            :key="index"
            class="quick-question-item"
            @click="selectQuickQuestion(question)"
          >
            {{ question }}
          </div>
        </div>

        <div class="tip" @click="goToFeedbackPage">
          AI生成内容可能出错，如有问题请反馈举报>>
        </div>
      </div>
      <!-- 答案 -->
      <div class="answer-item" v-if="!item.is_from_self && !item.issue">
        <!-- 答案信息 -->
        <!-- v-touch:tap="longPress(index, $event)" -->
        <div
          class="answer-info"
          @touchstart="handleTouchStart"
          @touchend="longPress(index, $event)"
          :ref="item.record_id"
          :id="item.session_id"
        >
          <div
            v-if="
              item.agent_thought &&
              item.agent_thought.procedures &&
              item.agent_thought.procedures.length > 0
            "
          >
            <!-- 思考部分   -->
            <MsgThought
              v-for="(thought, index) in item.agent_thought.procedures"
              :key="index"
              :content="thought.debugging.content"
              :title="thought.title"
              :titleIcon="thought.icon"
              :nodeName="thought.name"
              :status="thought.status"
              :elapsed="thought.elapsed"
              :detailVisible="thought.detailVisible"
            />
          </div>
          <div class="loading" v-if="item.loading_message">正在思考中</div>
          <!-- 回复主体 -->
          <MsgContent
            :showTags="true"
            :recordId="item.record_id"
            :isReplaceLinks="true"
            :loadingMessage="item.loading_message"
            :content="item.content"
            :isFinal="item.is_final"
            :isMdExpand="item.isMdExpand"
            @littleTagClick="littleTagClick"
          />
          <!-- 参考来源 -->
          <Reference
            v-if="item.references && item.references.length > 0"
            :references-list="item.references"
          />
          <!-- 运行状态 -->
          <TokensBoardBfr
            class="tokens-board-class"
            :showDtl="true"
            :tokensData="item.tokens_msg"
          ></TokensBoardBfr>
        </div>
      </div>
    </div>

    <!-- 操作菜单 -->
    <div
      v-if="showMenu"
      class="action-menu"
      :style="{ top: menuPosition.top + 'px', left: menuPosition.left + 'px' }"
      @touchstart.stop
    >
      <div class="menu-item" @click="copyMessage">
        <span class="icon iconfont icon-fuzhi"></span>
        复制
      </div>
      <div class="menu-item" @click="deleteMessage">
        <span class="icon iconfont icon-shanchu"></span>
        删除
      </div>
    </div>
    <!-- 遮罩层 -->
    <div v-if="showMenu" class="mask" @click="showMenu = !showMenu" />
  </div>
</template>

<script>
import Vue from "vue";
import clone from "clone";
import VueMarkdown from "vue-markdown";
import elementResizeDetectorMaker from "element-resize-detector";
import { scrollToBottom } from "@/utils/util";
import { MESSAGE_TYPE, ACCESS_TYPE } from "@/constants";
import TokensBoardBfr from "./tokens-board-brif.vue";
import Reference from "./reference-component.vue";
export default {
  name: "ClientChat",
  components: {
    VueMarkdown,
    Reference,
    TokensBoardBfr,
  },
  data() {
    return {
      popperList: [],
      oldPopDemo: null,
      loading: false,
      historyLoading: false,
      timestampGap: 5 * 60, // 两次问题间隔大于5min，则展示时间戳（接口侧返回的时间戳是秒级）
      msgList: [], // 对话消息列表
      robotName: "", // 机器人名称
      chatBoxHeight: document.body.clientHeight,
      jsScrolling: false,
      userScrolling: false,

      showMenu: false,
      menuPosition: { top: 0, left: 0 },
      currentSelectedIndex: 0,
      startTime: null,

      baseArr: [
        {
          role: "assistant",
          record_id: "EE1231",
          session_id: "EE1231",
          timestamp: +new Date(),
          content:
            "你好！我是AI助手，有什么可以帮您的吗？你可以直接点击下面的快捷问题开始提问。",
          loading: false,
        },
        {
          role: "assistant",
          record_id: "EE1231",
          session_id: "AA1231",
          timestamp: +new Date() + 1000,
          issue: true,
          loading: false,
          quickQuestionsLen: 2,
          quickQuestions: [
            [
              "APP使用前说明",
              "员工如何查看自己的业绩？",
              "怎么设置提成方案？",
              "如何作废单据？",
              "微商城推荐有礼说明",
            ],
            [
              "顾客按如何预约技师？",
              "如何连接打印机？",
              "如何正确登录APP？",
              "如何核销优惠券？",
              "次卡&套餐使用说明",
            ],
            [
              "如何删除会员？",
              "如何添加员工账号？",
              "如何积分兑换商品？",
              "如果设置“自选卡”?",
              "如何区分会员价、等级价、折扣价？",
            ],
          ],
        },
      ],
    };
  },
  created() {
    // 监听用户端/管理端体验侧的ws事件
    this.listenClientAndManageEvent();
    // 监听公共的ws事件
    this.listenCommonEvent();
  },
  mounted() {
    const erd = elementResizeDetectorMaker();
    const bodyDom = document.body;
    // let CHAT_LIST = JSON.parse(localStorage.getItem("CHAT_LIST"));
    // const msgList = CHAT_LIST || this.baseArr;
    // 示例初始消息
    this.msgList = this.baseArr;
    erd.listenTo(bodyDom, (element) => {
      this.chatBoxHeight = element.clientHeight - 113; // 57+56 头部的高度
    });

    document.addEventListener("click", this.handleOutsideClick);

    const sDom = document.querySelector(".client-chat");
    sDom.addEventListener("scroll", () => {
      if (
        this.msgList[this.msgList.length - 1].is_final === false &&
        !this.jsScrolling
      ) {
        this.userScrolling = true;
      } else {
        this.jsScrolling = false;
      }
    });
  },
  beforeDestroy() {
    // 移除全局事件监听器
    document.removeEventListener("click", this.handleOutsideClick);
  },
  methods: {
    // 内嵌微信
    /**
     * 调用原生方法
     * @param {string} method 方法名
     * @param {Object} params 参数对象
     */
    goToFeedbackPage() {
      // 生成唯一回调ID
      // 准备调用数据

      if (this.isIOS()) {
        // iOS调用方式
        window.webkit.messageHandlers.goToFeedbackPage.postMessage(null);
      } else if (this.isAndroid()) {
        window.android["goToFeedbackPage"]();
      } else {
        console.warn("原生环境");
        if (callback) callback({ error: "non_native_environment" });
      }
    },
    // 判断平台
    isIOS() {
      return /iPhone|iPad|iPod/i.test(navigator.userAgent);
    },
    isAndroid() {
      return /Android/i.test(navigator.userAgent);
    },

    longPress(index, e) {
      const endTime = Date.now(); // 记录触摸结束时间
      const duration = endTime - this.startTime; // 计算持续时间
      if (duration > 300) {
        // 例如，设置长按时间为500毫秒
        e.preventDefault();
        this.currentSelectedIndex = index;
        this.menuPosition = {
          top: e.changedTouches[0].pageY - 40,
          left: Math.min(
            e.changedTouches[0].pageX - 60,
            window.innerWidth - 120
          ),
        };
        this.showMenu = true;
      }
    },
    handleTouchStart() {
      this.startTime = Date.now(); // 记录触摸开始时间
    },
    // 选择快捷问题
    selectQuickQuestion(question) {
      this.$emit("send", question, this.msgList);
    },

    openSearchUrl(refer, index) {
      window.open(refer.url);
    },
    // 监听用户端/管理端体验侧的ws事件
    listenClientAndManageEvent() {
      // 从缓存获取机器人信息
      let cachedConfig = null;
      if (ACCESS_TYPE === "ws") {
        cachedConfig = this.$clientData.getConfigInfo();
      } else {
        cachedConfig = this.$SseCls.sseQueryConfigInfo();
      }
      if (cachedConfig) {
        this.robotName = cachedConfig.name;
      }

      // 监听答案消息队列变更事件
      this.$eventHub.$on("client_msgContentChange", (res) => {
        const { chatsContent, type } = res;
        // console.log("监听答案消息队列变更事件", chatsContent, this.msgList);
        // PS：若新消息不属于当前机器人，则在 $clientData 中监听到ws消息后判断并屏蔽。不在此处判断和屏蔽
        this.renderMsgList(chatsContent, type);
      });
    },
    // 监听公共的ws事件
    listenCommonEvent() {
      this.$eventHub.$on("data_history", () => {
        this.historyLoading = false;
      });

      this.$eventHub.$on("data_historyError", () => {
        this.historyLoading = false;
      });
    },
    // 渲染消息会话页面
    renderMsgList(data, type) {
      // 无需滚动至底部的ws事件：用户端拉取历史记录、用户端停止生成、坐席端取历史记录、点赞点踩
      const noScrollEvt = [
        MESSAGE_TYPE.HISTORY,
        MESSAGE_TYPE.STOP,
        MESSAGE_TYPE.WORKBENCH_HISTORY,
        MESSAGE_TYPE.FEEDBACK,
      ];
      const list = data.map((el) => {
        return { ...el, showPop: true };
      });
      this.msgList = clone(list);

      // 对话框滚动至底部（部分ws事件类型无需执行滚动）
      this.$nextTick(() => {
        const sDom = document.querySelector(".client-chat");

        if (!sDom) return;

        if (!this.userScrolling && !noScrollEvt.includes(type)) {
          this.jsScrolling = true;
          scrollToBottom(sDom, sDom.scrollHeight);
        }
        if (
          this.msgList.length > 0 &&
          this.msgList[this.msgList.length - 1].is_final === true
        ) {
          // 最后保存本地呢
          // localStorage.setItem(
          //   "CHAT_LIST",
          //   JSON.stringify([...this.baseArr, ...this.msgList])
          // );
          this.userScrolling = false;
        }
      });
    },
    handleOutsideClick(event) {
      if (!this.oldPopDemo) {
        return;
      }
      const firstElement = document.getElementsByClassName("pop-demo")[0];
      if (
        this.oldPopDemo.contains(event.target) ||
        firstElement.contains(event.target)
      ) {
      } else {
        if (this.oldPopDemo) {
          this.$refs["first"] &&
            this.$refs["first"].unbindTrigger(this.oldPopDemo);
        }
        // 调用你想要执行的方法
        this.$refs["first"] && this.$refs["first"].hide();
        this.oldPopDemo = null;
      }
    },
    littleTagClick(e, r) {
      const findMsg = this.$clientData.getMsgById(r);
      let innerDome = e.querySelectorAll(".little-tags");
      let outerTextArr = [];
      if (innerDome && innerDome.length > 0) {
        innerDome.forEach((dom) => {
          outerTextArr.push(dom.outerText);
        });
      }
      this.popperList = findMsg.references.filter((e) =>
        outerTextArr.includes(e.id)
      );
      if (e) {
        this.$refs["first"] && this.$refs["first"].bindTrigger(e, "manual");
        this.$refs["first"] && this.$refs["first"].update();
        this.$refs["first"] && this.$refs["first"].show();
        this.oldPopDemo = e;
      }
    },

    // 点击复制
    async copyMessage() {
      if (this.currentSelectedIndex >= 0) {
        const content = this.msgList[this.currentSelectedIndex].content;
        //  复制代码
        try {
          await navigator.clipboard.writeText(content);
          Vue.prototype.$message("复制成功！", "success", null, 2000);
        } catch (err) {
          Vue.prototype.$message("复制失败！", "error", null, 2000);
        }
      }
      this.showMenu = false;
    },

    // 删除对应消息
    deleteMessage() {
      if (this.currentSelectedIndex >= 0) {
        this.msgList.splice(this.currentSelectedIndex, 1);
      }
      // localStorage.setItem("CHAT_LIST", JSON.stringify(this.msgList));
      this.showMenu = false;
    },
  },
};
</script>

<style lang="less">
.client-chat::-webkit-scrollbar {
  display: none;
}
.pop-demo {
  // background-color: pink;
  padding: 10px;
  display: flex;
  min-width: var(--size-l);
  padding: var(--spacing-base);
  flex-direction: column;
  justify-content: center;
  // align-items: center;
  gap: var(--spacing-tight);

  border-radius: var(--radius-normal);
  border: 0.5px solid var(--color-border-normal);

  background: var(--color-bg-2);
  /* shadow/--shadow-medium */
  box-shadow: var(--shadow-medium-x-1) var(--shadow-medium-y-2)
      var(--shadow-medium-blur-1) var(--shadow-medium-spread-1)
      var(--shadow-medium-color-1),
    var(--shadow-medium-x-2) var(--shadow-medium-y-2)
      var(--shadow-medium-blur-2) var(--shadow-medium-spread-2)
      var(--shadow-medium-color-2),
    var(--shadow-medium-x-3) var(--shadow-medium-y-3)
      var(--shadow-medium-blur-3) var(--shadow-medium-spread-3)
      var(--shadow-medium-color-3);

  .v-popper__arrow {
    display: block;
  }
  .pop-demo-list {
    color: var(--color-link-normal);
    /* caption/--caption-regular */
    font-family: var(--font-family-normal);
    font-size: 12px;
    font-style: normal;
    font-weight: 400;
    line-height: 16px; /* 133.333% */
    .v-button {
      text-decoration: none;
      text-align: left;
    }
  }
}
.client-chat {
  display: flex;
  flex-direction: column;
  flex: 1;
  overflow-y: overlay;
  padding: 0 12px;

  .loading {
    margin: 1em 0;
    width: 150px;

    &:after {
      content: ".";
      animation: ellipsis 1.5s steps(1, end) infinite;
    }
  }

  @keyframes ellipsis {
    0% {
      content: ".";
    }

    33% {
      content: "..";
    }

    66% {
      content: "...";
    }

    100% {
      content: ".";
    }
  }

  .qa-item {
    display: flex;
    flex-direction: column;
    margin-bottom: 16px;
    font-weight: 400;
    font-size: 14px;
    color: var(--color-text-primary);

    .timestamp {
      font-weight: 400;
      font-size: 12px;
      line-height: 16px;
      text-align: center;
      color: var(--color-text-caption);
      margin: 16px 0;
    }

    .question-item {
      display: flex;
      align-items: center;
      width: fit-content;
      text-align: center;
      align-self: flex-end;
      padding-left: 44px;

      .qs-error {
        min-width: 16px;
        margin-right: 10px;
        color: var(--color-error-normal);
      }

      .qs-loading {
        margin-right: 10px;
      }

      .question-text {
        background: #2870ea;
        color: #fff;
        border-radius: 6px;
        padding: 0 12px;
        text-align: left;
        word-break: break-all;
        word-wrap: break-word;

        code {
          white-space: break-spaces;
        }

        img {
          max-width: 80%;
        }
      }
    }

    .summary-item {
      align-self: center;
      margin: 12px 0;
    }

    .answer-item {
      display: flex;

      .contacter-avatar {
        width: 32px;
        height: 32px;
        border-radius: 50%;
        margin-right: 12px;
        display: flex;
        align-items: center;
        justify-content: center;
      }

      .answer-info {
        position: relative;
        display: flex;
        flex-direction: column;
        padding: 0 12px;
        background: #f4f5f7;
        border-radius: 6px;
        width: calc(100% - 67px);

        .answer-expand {
          display: flex;
          justify-content: center;
          align-items: center;
          cursor: pointer;
          width: 44px;
          height: 24px;
          margin-bottom: 12px;
          background: var(--color-bg-2);
          box-shadow: var(--shadow-small-light);
          border-radius: 16px;
          align-self: center;
        }

        .stop-ws {
          color: var(--color-text-caption);
          margin-left: 5px;
        }

        .answer-source {
          margin: 12px 0;
          font-size: 14px;
          color: var(--color-text-caption);
          text-align: left;

          .v-button {
            text-decoration: none;
            text-align: left;
          }
        }
      }
    }
  }

  .qa-item:last-child {
    padding-bottom: 120px;
  }
}

.chat-content1 {
  .tip {
    color: #7d7d7d;
    font-size: 13px;
    height: 30px;
    display: flex;
    justify-content: center;
    align-items: center;
  }
  .quick-questions {
    overflow: hidden;
    overflow-x: auto;
    white-space: nowrap;
    padding: 7px 0;
    box-sizing: border-box;
    .quick-question-item {
      margin-bottom: 12px;
      display: inline-block;
      padding: 6px 12px;
      margin-right: 10px;
      background-color: #dbeafe;
      border-radius: 20px;
      color: #1a8cff;
      font-size: 13px;
      cursor: pointer;
      box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
    }

    &::-webkit-scrollbar {
      display: none;
    }
  }
}

.action-menu {
  position: fixed;
  background-color: #fff;
  border-radius: 6px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15);
  z-index: 1000;
  overflow: hidden;
  .icon {
    margin-right: 10px;
  }

  .menu-item {
    padding: 12px 20px;
    font-size: 14px;
    color: #333;
    border-bottom: 1px solid #f5f5f5;
  }

  .menu-item:last-child {
    border-bottom: none;
  }

  .menu-item:active {
    background-color: #f5f5f5;
  }
}
.mask {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: transparent;
  z-index: 999;
  display: flex;
}
</style>
